// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.

// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity Ethereum.  If not, see <http://www.gnu.org/licenses/>.

//! VM Instructions list and utility functions

pub use self::Instruction::*;

macro_rules! enum_with_from_u8 {
	(
		$( #[$enum_attr:meta] )*
		pub enum $name:ident {
			$( $( #[$variant_attr:meta] )* $variant:ident = $discriminator:expr ),+,
		}
	) => {
		$( #[$enum_attr] )*
		pub enum $name {
			$( $( #[$variant_attr] )* $variant = $discriminator ),+,
		}

		impl $name {
			#[doc = "Convert from u8 to the given enum"]
			pub fn from_u8(value: u8) -> Option<Self> {
				match value {
					$( $discriminator => Some($variant) ),+,
					_ => None,
				}
			}
		}
	};
}

enum_with_from_u8! {
	#[doc = "Virtual machine bytecode instruction."]
	#[repr(u8)]
	#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug, Hash)]
	pub enum Instruction {
		#[doc = "halts execution"]
		STOP = 0x00,
		#[doc = "addition operation"]
		ADD = 0x01,
		#[doc = "mulitplication operation"]
		MUL = 0x02,
		#[doc = "subtraction operation"]
		SUB = 0x03,
		#[doc = "integer division operation"]
		DIV = 0x04,
		#[doc = "signed integer division operation"]
		SDIV = 0x05,
		#[doc = "modulo remainder operation"]
		MOD = 0x06,
		#[doc = "signed modulo remainder operation"]
		SMOD = 0x07,
		#[doc = "unsigned modular addition"]
		ADDMOD = 0x08,
		#[doc = "unsigned modular multiplication"]
		MULMOD = 0x09,
		#[doc = "exponential operation"]
		EXP = 0x0a,
		#[doc = "extend length of signed integer"]
		SIGNEXTEND = 0x0b,

		#[doc = "less-than comparision"]
		LT = 0x10,
		#[doc = "greater-than comparision"]
		GT = 0x11,
		#[doc = "signed less-than comparision"]
		SLT = 0x12,
		#[doc = "signed greater-than comparision"]
		SGT = 0x13,
		#[doc = "equality comparision"]
		EQ = 0x14,
		#[doc = "simple not operator"]
		ISZERO = 0x15,
		#[doc = "bitwise AND operation"]
		AND = 0x16,
		#[doc = "bitwise OR operation"]
		OR = 0x17,
		#[doc = "bitwise XOR operation"]
		XOR = 0x18,
		#[doc = "bitwise NOT opertation"]
		NOT = 0x19,
		#[doc = "retrieve single byte from word"]
		BYTE = 0x1a,
		#[doc = "shift left operation"]
		SHL = 0x1b,
		#[doc = "logical shift right operation"]
		SHR = 0x1c,
		#[doc = "arithmetic shift right operation"]
		SAR = 0x1d,

		#[doc = "compute SHA3-256 hash"]
		SHA3 = 0x20,

		#[doc = "get address of currently executing account"]
		ADDRESS = 0x30,
		#[doc = "get balance of the given account"]
		BALANCE = 0x31,
		#[doc = "get execution origination address"]
		ORIGIN = 0x32,
		#[doc = "get caller address"]
		CALLER = 0x33,
		#[doc = "get deposited value by the instruction/transaction responsible for this execution"]
		CALLVALUE = 0x34,
		#[doc = "get input data of current environment"]
		CALLDATALOAD = 0x35,
		#[doc = "get size of input data in current environment"]
		CALLDATASIZE = 0x36,
		#[doc = "copy input data in current environment to memory"]
		CALLDATACOPY = 0x37,
		#[doc = "get size of code running in current environment"]
		CODESIZE = 0x38,
		#[doc = "copy code running in current environment to memory"]
		CODECOPY = 0x39,
		#[doc = "get price of gas in current environment"]
		GASPRICE = 0x3a,
		#[doc = "get external code size (from another contract)"]
		EXTCODESIZE = 0x3b,
		#[doc = "copy external code (from another contract)"]
		EXTCODECOPY = 0x3c,
		#[doc = "get the size of the return data buffer for the last call"]
		RETURNDATASIZE = 0x3d,
		#[doc = "copy return data buffer to memory"]
		RETURNDATACOPY = 0x3e,
		#[doc = "return the keccak256 hash of contract code"]
		EXTCODEHASH = 0x3f,

		#[doc = "get hash of most recent complete block"]
		BLOCKHASH = 0x40,
		#[doc = "get the block's coinbase address"]
		COINBASE = 0x41,
		#[doc = "get the block's timestamp"]
		TIMESTAMP = 0x42,
		#[doc = "get the block's number"]
		NUMBER = 0x43,
		#[doc = "get the block's difficulty"]
		DIFFICULTY = 0x44,
		#[doc = "get the block's gas limit"]
		GASLIMIT = 0x45,
		#[doc = "get chain ID"]
		CHAINID = 0x46,
		#[doc = "get balance of own account"]
		SELFBALANCE = 0x47,

		#[doc = "remove item from stack"]
		POP = 0x50,
		#[doc = "load word from memory"]
		MLOAD = 0x51,
		#[doc = "save word to memory"]
		MSTORE = 0x52,
		#[doc = "save byte to memory"]
		MSTORE8 = 0x53,
		#[doc = "load word from storage"]
		SLOAD = 0x54,
		#[doc = "save word to storage"]
		SSTORE = 0x55,
		#[doc = "alter the program counter"]
		JUMP = 0x56,
		#[doc = "conditionally alter the program counter"]
		JUMPI = 0x57,
		#[doc = "get the program counter"]
		PC = 0x58,
		#[doc = "get the size of active memory"]
		MSIZE = 0x59,
		#[doc = "get the amount of available gas"]
		GAS = 0x5a,
		#[doc = "set a potential jump destination"]
		JUMPDEST = 0x5b,

		#[doc = "place 1 byte item on stack"]
		PUSH1 = 0x60,
		#[doc = "place 2 byte item on stack"]
		PUSH2 = 0x61,
		#[doc = "place 3 byte item on stack"]
		PUSH3 = 0x62,
		#[doc = "place 4 byte item on stack"]
		PUSH4 = 0x63,
		#[doc = "place 5 byte item on stack"]
		PUSH5 = 0x64,
		#[doc = "place 6 byte item on stack"]
		PUSH6 = 0x65,
		#[doc = "place 7 byte item on stack"]
		PUSH7 = 0x66,
		#[doc = "place 8 byte item on stack"]
		PUSH8 = 0x67,
		#[doc = "place 9 byte item on stack"]
		PUSH9 = 0x68,
		#[doc = "place 10 byte item on stack"]
		PUSH10 = 0x69,
		#[doc = "place 11 byte item on stack"]
		PUSH11 = 0x6a,
		#[doc = "place 12 byte item on stack"]
		PUSH12 = 0x6b,
		#[doc = "place 13 byte item on stack"]
		PUSH13 = 0x6c,
		#[doc = "place 14 byte item on stack"]
		PUSH14 = 0x6d,
		#[doc = "place 15 byte item on stack"]
		PUSH15 = 0x6e,
		#[doc = "place 16 byte item on stack"]
		PUSH16 = 0x6f,
		#[doc = "place 17 byte item on stack"]
		PUSH17 = 0x70,
		#[doc = "place 18 byte item on stack"]
		PUSH18 = 0x71,
		#[doc = "place 19 byte item on stack"]
		PUSH19 = 0x72,
		#[doc = "place 20 byte item on stack"]
		PUSH20 = 0x73,
		#[doc = "place 21 byte item on stack"]
		PUSH21 = 0x74,
		#[doc = "place 22 byte item on stack"]
		PUSH22 = 0x75,
		#[doc = "place 23 byte item on stack"]
		PUSH23 = 0x76,
		#[doc = "place 24 byte item on stack"]
		PUSH24 = 0x77,
		#[doc = "place 25 byte item on stack"]
		PUSH25 = 0x78,
		#[doc = "place 26 byte item on stack"]
		PUSH26 = 0x79,
		#[doc = "place 27 byte item on stack"]
		PUSH27 = 0x7a,
		#[doc = "place 28 byte item on stack"]
		PUSH28 = 0x7b,
		#[doc = "place 29 byte item on stack"]
		PUSH29 = 0x7c,
		#[doc = "place 30 byte item on stack"]
		PUSH30 = 0x7d,
		#[doc = "place 31 byte item on stack"]
		PUSH31 = 0x7e,
		#[doc = "place 32 byte item on stack"]
		PUSH32 = 0x7f,

		#[doc = "copies the highest item in the stack to the top of the stack"]
		DUP1 = 0x80,
		#[doc = "copies the second highest item in the stack to the top of the stack"]
		DUP2 = 0x81,
		#[doc = "copies the third highest item in the stack to the top of the stack"]
		DUP3 = 0x82,
		#[doc = "copies the 4th highest item in the stack to the top of the stack"]
		DUP4 = 0x83,
		#[doc = "copies the 5th highest item in the stack to the top of the stack"]
		DUP5 = 0x84,
		#[doc = "copies the 6th highest item in the stack to the top of the stack"]
		DUP6 = 0x85,
		#[doc = "copies the 7th highest item in the stack to the top of the stack"]
		DUP7 = 0x86,
		#[doc = "copies the 8th highest item in the stack to the top of the stack"]
		DUP8 = 0x87,
		#[doc = "copies the 9th highest item in the stack to the top of the stack"]
		DUP9 = 0x88,
		#[doc = "copies the 10th highest item in the stack to the top of the stack"]
		DUP10 = 0x89,
		#[doc = "copies the 11th highest item in the stack to the top of the stack"]
		DUP11 = 0x8a,
		#[doc = "copies the 12th highest item in the stack to the top of the stack"]
		DUP12 = 0x8b,
		#[doc = "copies the 13th highest item in the stack to the top of the stack"]
		DUP13 = 0x8c,
		#[doc = "copies the 14th highest item in the stack to the top of the stack"]
		DUP14 = 0x8d,
		#[doc = "copies the 15th highest item in the stack to the top of the stack"]
		DUP15 = 0x8e,
		#[doc = "copies the 16th highest item in the stack to the top of the stack"]
		DUP16 = 0x8f,

		#[doc = "swaps the highest and second highest value on the stack"]
		SWAP1 = 0x90,
		#[doc = "swaps the highest and third highest value on the stack"]
		SWAP2 = 0x91,
		#[doc = "swaps the highest and 4th highest value on the stack"]
		SWAP3 = 0x92,
		#[doc = "swaps the highest and 5th highest value on the stack"]
		SWAP4 = 0x93,
		#[doc = "swaps the highest and 6th highest value on the stack"]
		SWAP5 = 0x94,
		#[doc = "swaps the highest and 7th highest value on the stack"]
		SWAP6 = 0x95,
		#[doc = "swaps the highest and 8th highest value on the stack"]
		SWAP7 = 0x96,
		#[doc = "swaps the highest and 9th highest value on the stack"]
		SWAP8 = 0x97,
		#[doc = "swaps the highest and 10th highest value on the stack"]
		SWAP9 = 0x98,
		#[doc = "swaps the highest and 11th highest value on the stack"]
		SWAP10 = 0x99,
		#[doc = "swaps the highest and 12th highest value on the stack"]
		SWAP11 = 0x9a,
		#[doc = "swaps the highest and 13th highest value on the stack"]
		SWAP12 = 0x9b,
		#[doc = "swaps the highest and 14th highest value on the stack"]
		SWAP13 = 0x9c,
		#[doc = "swaps the highest and 15th highest value on the stack"]
		SWAP14 = 0x9d,
		#[doc = "swaps the highest and 16th highest value on the stack"]
		SWAP15 = 0x9e,
		#[doc = "swaps the highest and 17th highest value on the stack"]
		SWAP16 = 0x9f,

		#[doc = "Makes a log entry, no topics."]
		LOG0 = 0xa0,
		#[doc = "Makes a log entry, 1 topic."]
		LOG1 = 0xa1,
		#[doc = "Makes a log entry, 2 topics."]
		LOG2 = 0xa2,
		#[doc = "Makes a log entry, 3 topics."]
		LOG3 = 0xa3,
		#[doc = "Makes a log entry, 4 topics."]
		LOG4 = 0xa4,

		#[doc = "create a new account with associated code"]
		CREATE = 0xf0,
		#[doc = "message-call into an account"]
		CALL = 0xf1,
		#[doc = "message-call with another account's code only"]
		CALLCODE = 0xf2,
		#[doc = "halt execution returning output data"]
		RETURN = 0xf3,
		#[doc = "like CALLCODE but keeps caller's value and sender"]
		DELEGATECALL = 0xf4,
		#[doc = "create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160"]
		CREATE2 = 0xf5,
		#[doc = "stop execution and revert state changes. Return output data."]
		REVERT = 0xfd,
		#[doc = "like CALL but it does not take value, nor modify the state"]
		STATICCALL = 0xfa,
		#[doc = "halt execution and register account for later deletion"]
		SUICIDE = 0xff,
	}
}

impl Instruction {
	/// Returns true if given instruction is `PUSHN` instruction.
	pub fn is_push(&self) -> bool {
		*self >= PUSH1 && *self <= PUSH32
	}

	/// Returns number of bytes to read for `PUSHN` instruction
	/// PUSH1 -> 1
	pub fn push_bytes(&self) -> Option<usize> {
		if self.is_push() {
			Some(((*self as u8) - (PUSH1 as u8) + 1) as usize)
		} else {
			None
		}
	}

	/// Returns stack position of item to duplicate
	/// DUP1 -> 0
	pub fn dup_position(&self) -> Option<usize> {
		if *self >= DUP1 && *self <= DUP16 {
			Some(((*self as u8) - (DUP1 as u8)) as usize)
		} else {
			None
		}
	}

	/// Returns stack position of item to SWAP top with
	/// SWAP1 -> 1
	pub fn swap_position(&self) -> Option<usize> {
		if *self >= SWAP1 && *self <= SWAP16 {
			Some(((*self as u8) - (SWAP1 as u8) + 1) as usize)
		} else {
			None
		}
	}

	/// Returns number of topics to take from stack
	/// LOG0 -> 0
	pub fn log_topics(&self) -> Option<usize> {
		if *self >= LOG0 && *self <= LOG4 {
			Some(((*self as u8) - (LOG0 as u8)) as usize)
		} else {
			None
		}
	}

	/// Returns the instruction info.
	pub fn info(&self) -> &'static InstructionInfo {
		INSTRUCTIONS[*self as usize].as_ref().expect("A instruction is defined in Instruction enum, but it is not found in InstructionInfo struct; this indicates a logic failure in the code.")
	}
}

#[derive(PartialEq, Clone, Copy)]
pub enum GasPriceTier {
	/// 0 Zero
	Zero,
	/// 2 Quick
	Base,
	/// 3 Fastest
	VeryLow,
	/// 5 Fast
	Low,
	/// 8 Mid
	Mid,
	/// 10 Slow
	High,
	/// 20 Ext
	Ext,
	/// Multiparam or otherwise special
	Special,
}

impl GasPriceTier {
	/// Returns the index in schedule for specific `GasPriceTier`
	pub fn idx(&self) -> usize {
		match self {
			&GasPriceTier::Zero => 0,
			&GasPriceTier::Base => 1,
			&GasPriceTier::VeryLow => 2,
			&GasPriceTier::Low => 3,
			&GasPriceTier::Mid => 4,
			&GasPriceTier::High => 5,
			&GasPriceTier::Ext => 6,
			&GasPriceTier::Special => 7,
		}
	}
}

/// EVM instruction information.
#[derive(Copy, Clone)]
pub struct InstructionInfo {
	/// Mnemonic name.
	pub name: &'static str,
	/// Number of stack arguments.
	pub args: usize,
	/// Number of returned stack items.
	pub ret: usize,
	/// Gas price tier.
	pub tier: GasPriceTier
}

impl InstructionInfo {
	/// Create new instruction info.
	pub fn new(name: &'static str, args: usize, ret: usize, tier: GasPriceTier) -> Self {
		InstructionInfo { name, args, ret, tier }
	}
}

lazy_static! {
	/// Static instruction table.
	static ref INSTRUCTIONS: [Option<InstructionInfo>; 0x100] = {
		let mut arr = [None; 0x100];
		arr[STOP as usize] = Some(InstructionInfo::new("STOP", 0, 0, GasPriceTier::Zero));
		arr[ADD as usize] = Some(InstructionInfo::new("ADD", 2, 1, GasPriceTier::VeryLow));
		arr[SUB as usize] = Some(InstructionInfo::new("SUB", 2, 1, GasPriceTier::VeryLow));
		arr[MUL as usize] = Some(InstructionInfo::new("MUL", 2, 1, GasPriceTier::Low));
		arr[DIV as usize] = Some(InstructionInfo::new("DIV", 2, 1, GasPriceTier::Low));
		arr[SDIV as usize] = Some(InstructionInfo::new("SDIV", 2, 1, GasPriceTier::Low));
		arr[MOD as usize] = Some(InstructionInfo::new("MOD", 2, 1, GasPriceTier::Low));
		arr[SMOD as usize] = Some(InstructionInfo::new("SMOD", 2, 1, GasPriceTier::Low));
		arr[EXP as usize] = Some(InstructionInfo::new("EXP", 2, 1, GasPriceTier::Special));
		arr[NOT as usize] = Some(InstructionInfo::new("NOT", 1, 1, GasPriceTier::VeryLow));
		arr[LT as usize] = Some(InstructionInfo::new("LT", 2, 1, GasPriceTier::VeryLow));
		arr[GT as usize] = Some(InstructionInfo::new("GT", 2, 1, GasPriceTier::VeryLow));
		arr[SLT as usize] = Some(InstructionInfo::new("SLT", 2, 1, GasPriceTier::VeryLow));
		arr[SGT as usize] = Some(InstructionInfo::new("SGT", 2, 1, GasPriceTier::VeryLow));
		arr[EQ as usize] = Some(InstructionInfo::new("EQ", 2, 1, GasPriceTier::VeryLow));
		arr[ISZERO as usize] = Some(InstructionInfo::new("ISZERO", 1, 1, GasPriceTier::VeryLow));
		arr[AND as usize] = Some(InstructionInfo::new("AND", 2, 1, GasPriceTier::VeryLow));
		arr[OR as usize] = Some(InstructionInfo::new("OR", 2, 1, GasPriceTier::VeryLow));
		arr[XOR as usize] = Some(InstructionInfo::new("XOR", 2, 1, GasPriceTier::VeryLow));
		arr[BYTE as usize] = Some(InstructionInfo::new("BYTE", 2, 1, GasPriceTier::VeryLow));
		arr[SHL as usize] = Some(InstructionInfo::new("SHL", 2, 1, GasPriceTier::VeryLow));
		arr[SHR as usize] = Some(InstructionInfo::new("SHR", 2, 1, GasPriceTier::VeryLow));
		arr[SAR as usize] = Some(InstructionInfo::new("SAR", 2, 1, GasPriceTier::VeryLow));
		arr[ADDMOD as usize] = Some(InstructionInfo::new("ADDMOD", 3, 1, GasPriceTier::Mid));
		arr[MULMOD as usize] = Some(InstructionInfo::new("MULMOD", 3, 1, GasPriceTier::Mid));
		arr[SIGNEXTEND as usize] = Some(InstructionInfo::new("SIGNEXTEND", 2, 1, GasPriceTier::Low));
		arr[RETURNDATASIZE as usize] = Some(InstructionInfo::new("RETURNDATASIZE", 0, 1, GasPriceTier::Base));
		arr[RETURNDATACOPY as usize] = Some(InstructionInfo::new("RETURNDATACOPY", 3, 0, GasPriceTier::VeryLow));
		arr[SHA3 as usize] = Some(InstructionInfo::new("SHA3", 2, 1, GasPriceTier::Special));
		arr[ADDRESS as usize] = Some(InstructionInfo::new("ADDRESS", 0, 1, GasPriceTier::Base));
		arr[BALANCE as usize] = Some(InstructionInfo::new("BALANCE", 1, 1, GasPriceTier::Special));
		arr[ORIGIN as usize] = Some(InstructionInfo::new("ORIGIN", 0, 1, GasPriceTier::Base));
		arr[CALLER as usize] = Some(InstructionInfo::new("CALLER", 0, 1, GasPriceTier::Base));
		arr[CALLVALUE as usize] = Some(InstructionInfo::new("CALLVALUE", 0, 1, GasPriceTier::Base));
		arr[CALLDATALOAD as usize] = Some(InstructionInfo::new("CALLDATALOAD", 1, 1, GasPriceTier::VeryLow));
		arr[CALLDATASIZE as usize] = Some(InstructionInfo::new("CALLDATASIZE", 0, 1, GasPriceTier::Base));
		arr[CALLDATACOPY as usize] = Some(InstructionInfo::new("CALLDATACOPY", 3, 0, GasPriceTier::VeryLow));
		arr[EXTCODEHASH as usize] = Some(InstructionInfo::new("EXTCODEHASH", 1, 1, GasPriceTier::Special));
		arr[CODESIZE as usize] = Some(InstructionInfo::new("CODESIZE", 0, 1, GasPriceTier::Base));
		arr[CODECOPY as usize] = Some(InstructionInfo::new("CODECOPY", 3, 0, GasPriceTier::VeryLow));
		arr[GASPRICE as usize] = Some(InstructionInfo::new("GASPRICE", 0, 1, GasPriceTier::Base));
		arr[EXTCODESIZE as usize] = Some(InstructionInfo::new("EXTCODESIZE", 1, 1, GasPriceTier::Special));
		arr[EXTCODECOPY as usize] = Some(InstructionInfo::new("EXTCODECOPY", 4, 0, GasPriceTier::Special));
		arr[BLOCKHASH as usize] = Some(InstructionInfo::new("BLOCKHASH", 1, 1, GasPriceTier::Ext));
		arr[COINBASE as usize] = Some(InstructionInfo::new("COINBASE", 0, 1, GasPriceTier::Base));
		arr[TIMESTAMP as usize] = Some(InstructionInfo::new("TIMESTAMP", 0, 1, GasPriceTier::Base));
		arr[NUMBER as usize] = Some(InstructionInfo::new("NUMBER", 0, 1, GasPriceTier::Base));
		arr[DIFFICULTY as usize] = Some(InstructionInfo::new("DIFFICULTY", 0, 1, GasPriceTier::Base));
		arr[GASLIMIT as usize] = Some(InstructionInfo::new("GASLIMIT", 0, 1, GasPriceTier::Base));
		arr[CHAINID as usize] = Some(InstructionInfo::new("CHAINID", 0, 1, GasPriceTier::Base));
		arr[SELFBALANCE as usize] = Some(InstructionInfo::new("SELFBALANCE", 0, 1, GasPriceTier::Low));
		arr[POP as usize] = Some(InstructionInfo::new("POP", 1, 0, GasPriceTier::Base));
		arr[MLOAD as usize] = Some(InstructionInfo::new("MLOAD", 1, 1, GasPriceTier::VeryLow));
		arr[MSTORE as usize] = Some(InstructionInfo::new("MSTORE", 2, 0, GasPriceTier::VeryLow));
		arr[MSTORE8 as usize] = Some(InstructionInfo::new("MSTORE8", 2, 0, GasPriceTier::VeryLow));
		arr[SLOAD as usize] = Some(InstructionInfo::new("SLOAD", 1, 1, GasPriceTier::Special));
		arr[SSTORE as usize] = Some(InstructionInfo::new("SSTORE", 2, 0, GasPriceTier::Special));
		arr[JUMP as usize] = Some(InstructionInfo::new("JUMP", 1, 0, GasPriceTier::Mid));
		arr[JUMPI as usize] = Some(InstructionInfo::new("JUMPI", 2, 0, GasPriceTier::High));
		arr[PC as usize] = Some(InstructionInfo::new("PC", 0, 1, GasPriceTier::Base));
		arr[MSIZE as usize] = Some(InstructionInfo::new("MSIZE", 0, 1, GasPriceTier::Base));
		arr[GAS as usize] = Some(InstructionInfo::new("GAS", 0, 1, GasPriceTier::Base));
		arr[JUMPDEST as usize] = Some(InstructionInfo::new("JUMPDEST", 0, 0, GasPriceTier::Special));
		arr[PUSH1 as usize] = Some(InstructionInfo::new("PUSH1", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH2 as usize] = Some(InstructionInfo::new("PUSH2", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH3 as usize] = Some(InstructionInfo::new("PUSH3", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH4 as usize] = Some(InstructionInfo::new("PUSH4", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH5 as usize] = Some(InstructionInfo::new("PUSH5", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH6 as usize] = Some(InstructionInfo::new("PUSH6", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH7 as usize] = Some(InstructionInfo::new("PUSH7", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH8 as usize] = Some(InstructionInfo::new("PUSH8", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH9 as usize] = Some(InstructionInfo::new("PUSH9", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH10 as usize] = Some(InstructionInfo::new("PUSH10", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH11 as usize] = Some(InstructionInfo::new("PUSH11", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH12 as usize] = Some(InstructionInfo::new("PUSH12", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH13 as usize] = Some(InstructionInfo::new("PUSH13", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH14 as usize] = Some(InstructionInfo::new("PUSH14", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH15 as usize] = Some(InstructionInfo::new("PUSH15", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH16 as usize] = Some(InstructionInfo::new("PUSH16", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH17 as usize] = Some(InstructionInfo::new("PUSH17", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH18 as usize] = Some(InstructionInfo::new("PUSH18", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH19 as usize] = Some(InstructionInfo::new("PUSH19", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH20 as usize] = Some(InstructionInfo::new("PUSH20", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH21 as usize] = Some(InstructionInfo::new("PUSH21", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH22 as usize] = Some(InstructionInfo::new("PUSH22", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH23 as usize] = Some(InstructionInfo::new("PUSH23", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH24 as usize] = Some(InstructionInfo::new("PUSH24", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH25 as usize] = Some(InstructionInfo::new("PUSH25", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH26 as usize] = Some(InstructionInfo::new("PUSH26", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH27 as usize] = Some(InstructionInfo::new("PUSH27", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH28 as usize] = Some(InstructionInfo::new("PUSH28", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH29 as usize] = Some(InstructionInfo::new("PUSH29", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH30 as usize] = Some(InstructionInfo::new("PUSH30", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH31 as usize] = Some(InstructionInfo::new("PUSH31", 0, 1, GasPriceTier::VeryLow));
		arr[PUSH32 as usize] = Some(InstructionInfo::new("PUSH32", 0, 1, GasPriceTier::VeryLow));
		arr[DUP1 as usize] = Some(InstructionInfo::new("DUP1", 1, 2, GasPriceTier::VeryLow));
		arr[DUP2 as usize] = Some(InstructionInfo::new("DUP2", 2, 3, GasPriceTier::VeryLow));
		arr[DUP3 as usize] = Some(InstructionInfo::new("DUP3", 3, 4, GasPriceTier::VeryLow));
		arr[DUP4 as usize] = Some(InstructionInfo::new("DUP4", 4, 5, GasPriceTier::VeryLow));
		arr[DUP5 as usize] = Some(InstructionInfo::new("DUP5", 5, 6, GasPriceTier::VeryLow));
		arr[DUP6 as usize] = Some(InstructionInfo::new("DUP6", 6, 7, GasPriceTier::VeryLow));
		arr[DUP7 as usize] = Some(InstructionInfo::new("DUP7", 7, 8, GasPriceTier::VeryLow));
		arr[DUP8 as usize] = Some(InstructionInfo::new("DUP8", 8, 9, GasPriceTier::VeryLow));
		arr[DUP9 as usize] = Some(InstructionInfo::new("DUP9", 9, 10, GasPriceTier::VeryLow));
		arr[DUP10 as usize] = Some(InstructionInfo::new("DUP10", 10, 11, GasPriceTier::VeryLow));
		arr[DUP11 as usize] = Some(InstructionInfo::new("DUP11", 11, 12, GasPriceTier::VeryLow));
		arr[DUP12 as usize] = Some(InstructionInfo::new("DUP12", 12, 13, GasPriceTier::VeryLow));
		arr[DUP13 as usize] = Some(InstructionInfo::new("DUP13", 13, 14, GasPriceTier::VeryLow));
		arr[DUP14 as usize] = Some(InstructionInfo::new("DUP14", 14, 15, GasPriceTier::VeryLow));
		arr[DUP15 as usize] = Some(InstructionInfo::new("DUP15", 15, 16, GasPriceTier::VeryLow));
		arr[DUP16 as usize] = Some(InstructionInfo::new("DUP16", 16, 17, GasPriceTier::VeryLow));
		arr[SWAP1 as usize] = Some(InstructionInfo::new("SWAP1", 2, 2, GasPriceTier::VeryLow));
		arr[SWAP2 as usize] = Some(InstructionInfo::new("SWAP2", 3, 3, GasPriceTier::VeryLow));
		arr[SWAP3 as usize] = Some(InstructionInfo::new("SWAP3", 4, 4, GasPriceTier::VeryLow));
		arr[SWAP4 as usize] = Some(InstructionInfo::new("SWAP4", 5, 5, GasPriceTier::VeryLow));
		arr[SWAP5 as usize] = Some(InstructionInfo::new("SWAP5", 6, 6, GasPriceTier::VeryLow));
		arr[SWAP6 as usize] = Some(InstructionInfo::new("SWAP6", 7, 7, GasPriceTier::VeryLow));
		arr[SWAP7 as usize] = Some(InstructionInfo::new("SWAP7", 8, 8, GasPriceTier::VeryLow));
		arr[SWAP8 as usize] = Some(InstructionInfo::new("SWAP8", 9, 9, GasPriceTier::VeryLow));
		arr[SWAP9 as usize] = Some(InstructionInfo::new("SWAP9", 10, 10, GasPriceTier::VeryLow));
		arr[SWAP10 as usize] = Some(InstructionInfo::new("SWAP10", 11, 11, GasPriceTier::VeryLow));
		arr[SWAP11 as usize] = Some(InstructionInfo::new("SWAP11", 12, 12, GasPriceTier::VeryLow));
		arr[SWAP12 as usize] = Some(InstructionInfo::new("SWAP12", 13, 13, GasPriceTier::VeryLow));
		arr[SWAP13 as usize] = Some(InstructionInfo::new("SWAP13", 14, 14, GasPriceTier::VeryLow));
		arr[SWAP14 as usize] = Some(InstructionInfo::new("SWAP14", 15, 15, GasPriceTier::VeryLow));
		arr[SWAP15 as usize] = Some(InstructionInfo::new("SWAP15", 16, 16, GasPriceTier::VeryLow));
		arr[SWAP16 as usize] = Some(InstructionInfo::new("SWAP16", 17, 17, GasPriceTier::VeryLow));
		arr[LOG0 as usize] = Some(InstructionInfo::new("LOG0", 2, 0, GasPriceTier::Special));
		arr[LOG1 as usize] = Some(InstructionInfo::new("LOG1", 3, 0, GasPriceTier::Special));
		arr[LOG2 as usize] = Some(InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special));
		arr[LOG3 as usize] = Some(InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special));
		arr[LOG4 as usize] = Some(InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special));
		arr[CREATE as usize] = Some(InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special));
		arr[CALL as usize] = Some(InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special));
		arr[CALLCODE as usize] = Some(InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special));
		arr[RETURN as usize] = Some(InstructionInfo::new("RETURN", 2, 0, GasPriceTier::Zero));
		arr[DELEGATECALL as usize] = Some(InstructionInfo::new("DELEGATECALL", 6, 1, GasPriceTier::Special));
		arr[STATICCALL as usize] = Some(InstructionInfo::new("STATICCALL", 6, 1, GasPriceTier::Special));
		arr[SUICIDE as usize] = Some(InstructionInfo::new("SUICIDE", 1, 0, GasPriceTier::Special));
		arr[CREATE2 as usize] = Some(InstructionInfo::new("CREATE2", 4, 1, GasPriceTier::Special));
		arr[REVERT as usize] = Some(InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero));
		arr
	};
}

/// Maximal number of topics for log instructions
pub const MAX_NO_OF_TOPICS: usize = 4;

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn test_is_push() {
		assert!(PUSH1.is_push());
		assert!(PUSH32.is_push());
		assert!(!DUP1.is_push());
	}

	#[test]
	fn test_get_push_bytes() {
		assert_eq!(PUSH1.push_bytes(), Some(1));
		assert_eq!(PUSH3.push_bytes(), Some(3));
		assert_eq!(PUSH32.push_bytes(), Some(32));
	}

	#[test]
	fn test_get_dup_position() {
		assert_eq!(DUP1.dup_position(), Some(0));
		assert_eq!(DUP5.dup_position(), Some(4));
		assert_eq!(DUP10.dup_position(), Some(9));
	}

	#[test]
	fn test_get_swap_position() {
		assert_eq!(SWAP1.swap_position(), Some(1));
		assert_eq!(SWAP5.swap_position(), Some(5));
		assert_eq!(SWAP10.swap_position(), Some(10));
	}

	#[test]
	fn test_get_log_topics() {
		assert_eq!(LOG0.log_topics(), Some(0));
		assert_eq!(LOG2.log_topics(), Some(2));
		assert_eq!(LOG4.log_topics(), Some(4));
	}
}
